#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "lm4f120h5qr.h"
#include "UART.h"
#include "gpio.h"


#define PIN_0     (1 << 0)
#define PIN_1     (1 << 1)
#define PIN_2     (1 << 2)
#define PIN_3     (1 << 3)
#define PIN_4     (1 << 4)
#define PIN_5     (1 << 5)
#define PIN_6     (1 << 6)
#define PIN_7     (1 << 7)

#define PA6_SW2       PIN_6
#define PA7_SW3       PIN_7
#define PD2_SW4       PIN_2
#define PD3_SW5       PIN_3
#define PF1_SW6       PIN_1


#define PA0_U0_RX  PIN_0
#define PA1_U0_TX  PIN_1


enum UartBufParams {
	UART_BUFSIZ = 1024
};

struct UartBuf {
	char buf[UART_BUFSIZ];
	int h;
	int t;
};

struct UartBuf u0tx;
struct UartBuf u0rx;


//*****************************************************************************
// Handle UART interrupts
//*****************************************************************************
void UART0IntHandler()
{
	UART_PERIPH *u = (UART_PERIPH *)UART0;
	
	DisableInterrupts();
	
	// TODO: DECIDE IF TX OR RX!!!!
	
	// if SW buffer is empty, disable TX interrupts.
	// otherwise, write until HW buf is full or SW buf is empty.
	if (u0tx.h == u0tx.t) {
		u->UARTControl &= ~UART_CTL_UARTEN;
		u->IntMask &= ~UART_IM_TXIM;
		u->UARTControl |= UART_CTL_UARTEN;
	} else {
		while (u0tx.h != u0tx.t && !(u->Flag & UART_FR_TXFF)) {
			u->Data = u0tx.buf[u0tx.h++];
			u0tx.h %= UART_BUFSIZ;
		}
	}
	
	EnableInterrupts();
	
	u->IntClear |= UART_IM_TXIM;
		
}

void UART1IntHandler()
{
}

void UART2IntHandler()
{
}

//*****************************************************************************
// Configure UART
//*****************************************************************************

int uart0Tx(char *d)
{
	UART_PERIPH *u = (UART_PERIPH *)UART0;
	char *d0 = d;
	int i;
	
	// write 0 bytes and return.
	if(!(*d)) return 0;
	
	DisableInterrupts();
	
	// if UART isn't currently TXing, prime it.
	if (!(u->IntMask & UART_IM_TXIM)) {
		u->Data = *d++;
		
		u->UARTControl &= ~UART_CTL_UARTEN;
		for (i = 1000; i; i--);
		u->IntMask |= UART_IM_TXIM;
		u->UARTControl |= UART_CTL_UARTEN;
	}
	
	// write to SW buffer
	while (*d) {
		u0tx.buf[u0tx.t++] = *d++;
		u0tx.t %= UART_BUFSIZ;
		
		//buffer is full if tail is right behind head
		if((u0tx.t + 1) % UART_BUFSIZ == u0tx.h)
			break;
	}
	
	EnableInterrupts();
	
	//return # bytes written.
	return d - d0;
}

void uartTxPoll(uint32_t base, char *data)
{
  UART_PERIPH *myPeriph = (UART_PERIPH *)base;
  
  if ( data != 0)
  {
    while(*data != '\0')
    {
      while( ((myPeriph->Flag)&(UART_FR_TXFF)) != 0 );
      myPeriph->Data = *data;
      data++;
    }
  }
  return;
}

bool configUART(UART_PERIPH *myUart, uint32_t baudrate)
{
	//2. Disable UART during config
	myUart->UARTControl = 0;
	
	//3. Set baud
	switch (baudrate) {
	case 96000:
		myUart->IntegerBaudRateDiv = 520;
		myUart->FracBaudRateDiv = 53;
		break;
	case 115200:
		myUart->IntegerBaudRateDiv = 43;
		myUart->FracBaudRateDiv = 26;
		break;
	default:
		return false;
	}

	//4. Set RX interrupt level hi-ish, TX interrupt level low-ish
	//myUart->IntFIFOLevelSel = UART_IFLS_RX6_8 | UART_IFLS_TX2_8;
	
	//5. IRQ on RX, Don't IRQ on TX until you have something to send.
	myUart->IntMask = UART_IM_RXIM;
	
	//6. Configure the Line Control for 8-n-1 w/ FIFOs
	myUart->LineControl = UART_LCRH_WLEN_8;// | UART_LCRH_FEN;

	//7. Enable UART w/ TX and RX
	myUart->UARTControl =
	    UART_CTL_RXE | UART_CTL_TXE | UART_CTL_UARTEN;
	
	return true;
}

bool initUARTDebug(uint32_t baudrate)
{
	uint32_t delay;
	UART_PERIPH *myUart = (UART_PERIPH *) UART0;
	GPIO_PORT *GpioPortA = (GPIO_PORT *) 0x40004000;
	
	//1. Enable clock. spin wait for power up.
	SYSCTL_RCGCUART_R |= SYSCTL_RCGCUART_R0;
	for (delay = 10; delay; delay--);
	
	//[2-7]. Generic UART configuration
	if(!configUART(myUart, baudrate))
		return false;
	
	//8. Setup GPIO
	GpioPortA->AlternateFunctionSelect = PA0_U0_RX | PA1_U0_TX;
	GpioPortA->DigitalEnable = PA0_U0_RX | PA1_U0_TX;
	GpioPortA->PortControl = GPIO_PCTL_PA0_U0RX | GPIO_PCTL_PA1_U0TX;
	
	//9. Set NVIC priority for UART
	//hahah.... nope. maybe later.
	
	//10. Allow NVIC to handle UART IRQs
	NVIC_EN0_R |= NVIC_EN0_INT5;

	// Wait until the UART is avaiable, plus a little more?
	while (!(SYSCTL_PRUART_R & SYSCTL_PRUART_R0));
	for (delay = 50; delay; delay--);
	
	// configure the software buffers
	u0rx.h = u0rx.t = 0;
	u0tx.h = u0tx.t = 0;
	
	uart0Tx("test");

	return true;
}
